package com.wuze.cloud.service.impl;


import com.wuze.cloud.adapter.SysFileAdapter;
import com.wuze.cloud.adapter.SysFolderAdapter;
import com.wuze.cloud.context.UserContext;
import com.wuze.cloud.dao.*;
import com.wuze.cloud.enums.SysFileTypeEnum;
import com.wuze.cloud.exception.SysGlobalException;
import com.wuze.cloud.handler.FileHandler;
import com.wuze.cloud.pojo.*;
import com.wuze.cloud.request.*;
import com.wuze.cloud.response.PreCheckResponse;
import com.wuze.cloud.response.SysFileResponse;
import com.wuze.cloud.response.SysFileVO;
import com.wuze.cloud.service.SysFileService;
import com.wuze.cloud.service.SysStorageService;
import com.wuze.cloud.util.ShardToMultipartFile;
import com.wuze.cloud.util.DownLoadZipUtil;
import com.wuze.cloud.util.SnowFlakeIdWorker;
import com.wuze.cloud.util.UuidUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.assertj.core.util.Lists;
import org.assertj.core.util.Sets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

import static com.wuze.cloud.enums.SysFileTypeEnum.*;
import static com.wuze.cloud.enums.SysGlobalExceptionEnum.*;
import static com.wuze.cloud.enums.SysStateEnum.*;
import static com.wuze.cloud.enums.SysUserRoleEnum.ROOT;

/**
 * @author: wuze
 * @description: 文件
 * @date: 2023/02/20
 */
@Service
@Slf4j
public class SysFileServiceImpl implements SysFileService {

    @Value("${file.uploadFolder}")
    private String storePath;
    @Value("${file.zipFilePath}")
    private String zipFilePath;

    @Autowired
    private SysFileDao fileDao;

    @Autowired
    private SysFileShardDao fileShardDao;

    @Autowired
    private SysFileAdapter fileAdapter;

    @Autowired
    private SysFolderAdapter folderAdapter;

    @Autowired
    private SysFolderRelationDao folderRelationDao;

    @Autowired
    private SysStorageService storageService;

    @Autowired
    private SysStorageDao storageDao;

    @Autowired
    private Set<FileHandler> fileToHtmlHandlers;

    @Autowired
    private SysFolderDao folderDao;

    @Autowired
    private SysRecyclingDao recyclingDao;

    @Autowired
    private SysUserDao userDao;


    /**
     * 分页查询
     * @param query 用户分页查询请求实体
     * @return 文件分页信息
     */
    @Override
    public SysFileResponse findByPage(SysFileQuery query) {
        SysUserDO user = UserContext.getUser();
        SysFileResponse response = new SysFileResponse();
        Integer total = fileDao.countByUserId(user.getId(),
                query.getFolderIds(),
                CREATE.name(),
                query.getFileName(),
                SysFileTypeEnum.TypeOfCode(query.getType()));
        if (total == 0) {
            response.setTotal(total);
            response.setFiles(Lists.newArrayList());
            return response;
        }
        query.setUserId(user.getId());

        List<SysFileDO> files = fileDao.findByPage(query,
                CREATE.name(),
                SysFileTypeEnum.TypeOfCode(query.getType()));

        List<SysFileVO> vos = fileAdapter.buildSysFileVOS(files);
        response.setFiles(vos);
        response.setTotal(total);
        return response;
    }

    /**
     * 管理员分页查询
     * @param query 管理员分页查询请求实体
     * @return 文件分页信息
     */
    @Override
    public SysFileResponse findPageByAdmin(SysAdminFileQuery query) {
        //鉴权
        SysUserDO user = UserContext.getUser();
        if (!user.getRole().equals(ROOT.getCode())) {
            throw new SysGlobalException(USER_AUTH_ERR.getCode(), USER_AUTH_ERR.getMsg());
        }
        SysFileResponse response = new SysFileResponse();
        List<SysUserDO> users = userDao.findByName(query.getUserName());
        if(CollectionUtils.isEmpty(users)){
            response.setTotal(0);
            response.setFiles(Lists.newArrayList());
            return response;
        }
        List<Long> userIds = users.stream().map(SysUserDO::getId)
                .collect(Collectors.toList());
        Map<Long, String> map = users.stream()
                .collect(Collectors.toMap(SysUserDO::getId, SysUserDO::getUserName));

        Integer total = fileDao.countByAdmin(userIds,
                StateOfCode(query.getState()),
                query.getFileName(),
                TypeOfCode(query.getType()));
        if (total == 0) {
            response.setTotal(total);
            response.setFiles(Lists.newArrayList());
            return response;
        }
        List<SysFileDO> files = fileDao.findPageByAdmin(userIds,
                StateOfCode(query.getState()),
                query.getFileName(),
                TypeOfCode(query.getType()),
                query);
        List<SysFileVO> vos = fileAdapter.buildSysFileVOS(files);
        vos.forEach(vo->{
            vo.setUserName(map.get(vo.getUserId()));
        });
        response.setFiles(vos);
        response.setTotal(total);
        return response;
    }

    /**
     * 上传文件预检查
     * @param query 文件上传请求实体
     * @return 响应实体
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PreCheckResponse preCheck(SysFilePreCheckQuery query) {

        SysUserDO user = UserContext.getUser();
        //文件预检查请求校验
        verifyPreCheckDTO(query);
        //校验服务器中是否有相同文件
        PreCheckResponse response = findSameFile(query, user);
        if (response != null) {
            return response;
        }
        //返回文件断点
        return findBreakPoint(query);
    }

    /**
     * 查询文件断点
     * @param query 文件上传请求实体
     */
    private PreCheckResponse findBreakPoint(SysFilePreCheckQuery query) {
        PreCheckResponse response = new PreCheckResponse();
        List<SysFileShardDO> shards = fileShardDao.findByKey(query.getOpenKey());
        response.setExist(Boolean.FALSE);
        response.setIndex(shards.size());
        if (!CollectionUtils.isEmpty(shards)) {
            response.setPath(shards.get(0).getPath());
        }
        return response;
    }

    /**
     * 校验服务器中是否有相同文件
     * @param query 文件上传请求实体
     * @param user 用户信息
     */
    private PreCheckResponse findSameFile(SysFilePreCheckQuery query, SysUserDO user) {
        PreCheckResponse response = new PreCheckResponse();
        SysFileDO fileDO = fileDao.findByKey(query.getOpenKey());
        if (Objects.nonNull(fileDO)) {
            //存在则不需要再次上传
            fileDO.setId(SnowFlakeIdWorker.nextId());
            fileDO.setUserId(user.getId());
            fileDO.setFolderId(query.getFolderId());
            fileDO.setFileName(query.getFileName());
            fileDO.setState(CREATE.name());
            //保存文件
            fileDao.save(fileDO);
            //保存文件夹关系表
            folderRelationDao.add(folderAdapter.buildRelationDO(fileDO));
            //更新存储信息
            storageService.updateStorageInfo(fileDO);
            response.setExist(Boolean.TRUE);
            response.setPath(fileDO.getRealPath());
            return response;
        }
        return null;
    }

    /**
     * 文件预检查请求校验
     * @param query 文件上传请求实体
     */
    private void verifyPreCheckDTO(SysFilePreCheckQuery query) {
        SysUserDO user = UserContext.getUser();
        //校验文件名是否重复
        SysFileDO file = fileDao.findOne(query,CREATE.name());
        if (Objects.nonNull(file)) {
            throw new SysGlobalException(FILE_REPEAT.getCode(), FILE_REPEAT.getMsg());
        }
        //校验用户空间是否足够
        SysStorageDO storage = storageDao.findByUserId(user.getId());
        storage.setCurrSize(storage.getCurrSize()+query.getSize());
        if(storage.getCurrSize()>storage.getMaxSize()){
            throw new SysGlobalException(SPACE_NOT_ENOUGH.getCode(),SPACE_NOT_ENOUGH.getMsg());
        }

    }

    /**
     * 分片上传文件
     * @param query 分片块
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void upload(SysFileChunkQuery query) throws Exception {

        SysUserDO user = UserContext.getUser();
        //保存文件到本地
        String path = saveLoc(query);
        query.setPath(path);
        //持久化分片到数据库
        fileShardDao.add(fileAdapter.buildSysFileShardDO(query));
        if (!query.getShardIndex().equals(query.getShardTotal())) {
            return;
        }
        query.setUserId(user.getId());
        // 合并分片
        String virtualPath = merge(query);
        //持久化文件到数据库
        SysFileDO file = fileAdapter.buildSysFileDO(query);
        file.setVirtualPath(virtualPath);
        fileDao.save(file);
        //与文件夹建立联系
        folderRelationDao.add(folderAdapter.buildRelationDO(file));
        //修改存储信息表
        storageService.updateStorageInfo(file);
    }

    /**
     * 保存分片
     * @param query 分片块
     * @return 路径
     */
    private String saveLoc(SysFileChunkQuery query) throws IOException {
        SysUserDO user = UserContext.getUser();
        log.info("上传文件开始");
        String key = query.getOpenKey();
        String suffix = query.getSuffix();
        String data = query.getShard();
        MultipartFile shard = ShardToMultipartFile.shardToMultipart(data,query.getEncrypt());
        // 保存文件到本地
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String format = simpleDateFormat.format(date);
        log.info("日期信息：" + format);
        String fullPath = storePath + user.getUserName() + "/shard/";
        if (!new File(fullPath).exists()) {
            new File(fullPath).mkdirs();
        }
        String path;
        if (StringUtils.isEmpty(query.getPath())) {
            path = new StringBuffer(fullPath)
                    .append(key)
                    .append(suffix)
                    .toString();
        } else {
            path = query.getPath();
        }
        // course\6sfSqfOwzmik4A4icMYuUe.mp4
        String localPath = new StringBuffer(path)
                .append(".")
                .append(query.getShardIndex())
                .toString(); // course\6sfSqfOwzmik4A4icMYuUe.mp4.1
        File dest = new File(localPath);
        shard.transferTo(dest);
        log.info(dest.getAbsolutePath());
        log.info("保存文件记录开始");
        return path;
    }

    /**
     * 合并分片
     * @param query 分片块
     * @return 虚拟路径
     */
    private String merge(SysFileChunkQuery query) throws Exception {
        log.info("合并分片开始");
        String path = query.getPath();
        query.setPath(query.getPath().replaceAll("shard/", ""));
        Integer shardTotal = query.getShardTotal();
        File newFile = new File(path.replaceAll("shard/", ""));
        FileOutputStream outputStream = new FileOutputStream(newFile, true);
        FileInputStream fileInputStream = null;
        byte[] byt = new byte[10 * 1024 * 1024];
        int len;
        try {
            for (int i = 0; i < shardTotal; i++) {
                // 读取第i个分片
                fileInputStream = new FileInputStream(path + "." + (i + 1));
                while ((len = fileInputStream.read(byt)) != -1) {
                    outputStream.write(byt, 0, len);
                }
            }
        } catch (IOException e) {
            log.error("分片合并异常", e);
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                outputStream.close();
                log.info("IO流关闭");
            } catch (Exception e) {
                log.error("IO流关闭", e);
            }
        }
        log.info("合并分片结束");

        // 删除分片 TODO 待优化（转移到定时任务处理）
        log.info("删除分片开始");
        File file;
        String filePath;
        for (int i = 0; i < shardTotal; i++) {
            filePath = path + "." + (i + 1);
            file = new File(filePath);
            System.gc();
            boolean result = file.delete();
            log.info("删除{}，{}", filePath, result ? "成功" : "失败");
        }
        log.info("删除分片结束");
        log.info("文件处理");
        FileHandler handler = getHandler(query.getSuffix());
        if (Objects.nonNull(handler)) {
            return handler.handler(query);
        } else {
            return "";
        }
    }

    /**
     * 获取文件处理器
     * @param suffix 文件后缀
     * @return FileHandler文件处理器
     */
    private FileHandler getHandler(String suffix) {
        suffix = suffix.toLowerCase();
        for (FileHandler fileToHtmlHandler : fileToHtmlHandlers) {
            if (fileToHtmlHandler.tags().contains(suffix)) {
                return fileToHtmlHandler;
            }
        }
        return null;
    }

    /**
     * 批量下载文件
     * @param query 下载请求实体
     * @param response HttpServletResponse
     */
    @Override
    public void batchDownload(SysDownloadQuery query, HttpServletResponse response) {

        //构建下载id
        Set<Long> ids = getDownloadIds(query);
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        log.info("下载文件ids:{}", ids);
        //记录下载日志 TODO

        //临时zip包名
        String packageName = UuidUtil.getUuid() + ".zip";
        //生成zip临时包
        createZip(ids, packageName);
        //二进制流输出-->下载
        down(response, packageName);
        //下载完成，删掉zip临时包
        deleteZip(packageName);
    }

    /**
     * 构建下载文件id
     * @param query 下载请求实体
     * @return 所有文件ID
     */
    private Set<Long> getDownloadIds(SysDownloadQuery query) {
        SysUserDO user = UserContext.getUser();
        Set<Long> ids = new HashSet<>(query.getFileIds());
        if(!CollectionUtils.isEmpty(query.getFolderIds())){
            List<Long> list = getFolderIdsByFolderIds(query.getFolderIds());
            List<SysFileDO> files = fileDao.findByFolderIds(list, user.getId() , CREATE.name());
            ids.addAll(files.stream().map(SysFileDO::getId).collect(Collectors.toSet()));
        }
        return ids;
    }

    /**
     * 创建临时zip包
     * @param ids 文件ID
     * @param packageName 临时包名
     */
    private void createZip(Set<Long> ids, String packageName) {
        log.info("生成临时zip文件");
        List<SysFileDO> files = fileDao.findByIds(ids,CREATE.name());
        List<SysFileVO> vos = fileAdapter.buildSysFileVOS(files);
        Map<Long, String> folderMap = vos.stream().collect(Collectors.toMap(SysFileVO::getId, SysFileVO::getFolderName));
        ZipOutputStream zipOut = null;
        try {
            zipOut = new ZipOutputStream(new FileOutputStream(zipFilePath + packageName));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        for (SysFileDO file : files) {
            try {
                String realFileName = folderMap.get(file.getId()) + "/" + file.getFileName();
                DownLoadZipUtil.fileToZip(file.getRealPath(), zipOut, realFileName);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        try {
            // 压缩完成后,关闭压缩流
            zipOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行下载
     * @param response HttpServletResponse
     * @param packageName 临时包名
     */
    private void down(HttpServletResponse response, String packageName) {
        log.info("开始下载");
        File file = new File(zipFilePath + packageName);

        try {

            //设置内容内容型应用下载，设置字符集
            response.setContentType("application/x-download;charset=utf-8");
            //告诉客户端该文件不是直接解析而是以附件的形式打开（下载）
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(packageName, "UTF-8"));
            response.setHeader("Content-Length", String.valueOf(file.length()));
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        //提高作用域
        ServletOutputStream outputStream;
        FileInputStream inputStream = null;
        try {
            //该流不可以手动关闭,手动关闭下载会出问题,下载完成后会自动关闭
            outputStream = response.getOutputStream();
            inputStream = new FileInputStream(file);
            //文件下载，复制
            IOUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 关闭输入流
        try {
            if (inputStream != null) {
                inputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("下载完成");
    }

    /**
     * 删除临时zip包
     * @param packageName 临时包名
     */
    private void deleteZip(String packageName) {
        log.info("开始删除zip文件");
        File fileTempZip = new File(zipFilePath + packageName);
        boolean delete = fileTempZip.delete();
        log.info("删除{}，{}", zipFilePath + packageName, delete ? "成功" : "失败");
    }

    /**
     * 获取文件夹及所有子文件夹ID
     * @param folderIds 文件夹ID
     * @return 所有文件夹ID
     */
    private List<Long> getFolderIdsByFolderIds(List<Long> folderIds){
        List<Long> result = Lists.newArrayList();
        if(CollectionUtils.isEmpty(folderIds)){
            return result;
        }
        result.addAll(folderIds);
        List<SysFolderRelationDO> relationDOS = folderRelationDao.findByPids(folderIds, FOLDER_FILE.name(),CREATE.name());
        if(CollectionUtils.isEmpty(relationDOS)){
            return result;
        }
        List<Long> ids = relationDOS.stream().map(SysFolderRelationDO::getChildId).collect(Collectors.toList());
        result.addAll(getFolderIdsByFolderIds(ids));
        return result;
    }

    /**
     * 批量删除文件
     * @param query 删除请求实体
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchdelete(SysDeleteFileQuery query) {
        //获取用户信息
        SysUserDO user = UserContext.getUser();
        //删除文件
        deleteFiles(query, user);
        //删除文件夹
        deleteFolders(query);
        //记录回收站
        addRecycling(query, user);
    }

    /**
     * 记录回收站
     * @param query 删除请求实体
     * @param user 用户信息
     */
    private void addRecycling(SysDeleteFileQuery query, SysUserDO user) {
        log.info("开始记录回收站");
        if(!CollectionUtils.isEmpty(query.getFileIds())){
            List<SysFileDO> fileDOS = fileDao.findByIds(Sets.newHashSet(query.getFileIds()), RECYCLING.name());
            fileDOS.forEach(file->{
                SysRecyclingDO recycling = new SysRecyclingDO();
                recycling.setId(SnowFlakeIdWorker.nextId());
                recycling.setFileId(file.getId());
                recycling.setUserId(user.getId());
                recycling.setName(file.getFileName());
                recycling.setState(RECYCLING.name());
                recyclingDao.add(recycling);
            });
        }
        if(!CollectionUtils.isEmpty(query.getFolderIds())){
            List<SysFolderDO> folderDOS = folderDao.findByIds(Sets.newHashSet(query.getFolderIds()));
            folderDOS.forEach(folder->{
                SysRecyclingDO recycling = new SysRecyclingDO();
                recycling.setId(SnowFlakeIdWorker.nextId());
                recycling.setFolderId(folder.getId());
                recycling.setUserId(user.getId());
                recycling.setName(folder.getFolderName());
                recycling.setState(RECYCLING.name());
                recyclingDao.add(recycling);
            });
        }
        log.info("记录完成fileId:{}", query.getFileIds());
        log.info("记录完成folderId:{}", query.getFolderIds());
    }

    /**
     * 删除文件夹
     * @param query 删除请求实体
     */
    private void deleteFolders(SysDeleteFileQuery query) {
        if(!CollectionUtils.isEmpty(query.getFolderIds())){
            //修改文件夹状态
            //获取所有文件夹id
            List<Long> folderIds = getFolderIdsByFolderIds(query.getFolderIds());
            log.info("开始删除文件夹，文件夹ids：{}", folderIds);

            //删除文件夹
            folderDao.batchUpdateFolderState(folderIds, RECYCLING.name());
            folderRelationDao.batchUpdateState(Sets.newHashSet(folderIds), RECYCLING.name());
            log.info("删除文件夹完成,文件夹id={}",folderIds);
        }
    }

    /**
     * 删除文件
     * @param query 删除请求实体
     * @param user 用户信息
     */
    private void deleteFiles(SysDeleteFileQuery query, SysUserDO user) {
        //构建删除id
        Set<Long> ids = getDeleteFileIds(query);
        if (!CollectionUtils.isEmpty(ids)) {
            log.info("开始删除文件，文件ids：{}", ids);
            List<SysFileDO> files = fileDao.findByIds(ids,CREATE.name());
            if(CollectionUtils.isEmpty(files)){
                throw new SysGlobalException(FILE_NOT_FOUND.getCode(),FILE_NOT_FOUND.getMsg());
            }
            //修改文件表状态
            fileDao.batchUpdateFileState(ids, RECYCLING.name());
            //修改文件夹关系表状态
            folderRelationDao.batchUpdateState(ids,RECYCLING.name());
            SysStorageDO storage = storageDao.findByUserId(user.getId());
            files.forEach(file -> {
                Long size = file.getSize();
                String type = file.getType();
                storage.setCurrSize(storage.getCurrSize() - size);
                if (type.equals(DOCUMENT_FILE.name())) {
                    storage.setDocumentSize(storage.getDocumentSize() - size);
                } else if (type.equals(VIDEO_FILE.name())) {
                    storage.setVideoSize(storage.getVideoSize() - size);
                } else if (type.equals(IMAGE_FILE.name())) {
                    storage.setImageSize(storage.getImageSize() - size);
                } else if (type.equals(OTHER_FILE.name())) {
                    storage.setOrtherSize(storage.getOrtherSize() - size);
                }

            });
            storageDao.update(storage);
            log.info("删除文件完成,文件id={}",ids);
        }
    }


    /**
     * 构建删除文件id
     * @param query
     * @return
     */
    private Set<Long> getDeleteFileIds(SysDeleteFileQuery query) {
        SysUserDO user = UserContext.getUser();
        Set<Long> ids = new HashSet<>(query.getFileIds());
        if(!CollectionUtils.isEmpty(query.getFolderIds())){
            List<Long> list = getFolderIdsByFolderIds(query.getFolderIds());
            List<SysFileDO> files = fileDao.findByFolderIds(list , user.getId() , CREATE.name());
            ids.addAll(files.stream().map(SysFileDO::getId).collect(Collectors.toSet()));
        }
        return ids;
    }
    /**
     * 修改文件
     * @param query
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(SysUpdateFileQuery query) {

        //请求校验
        SysFileDO file = verifyUpdateQuery(query);

        log.info("文件修改开始，file={}",file);
        //修改文件夹关系表
        folderRelationDao.update(query.getId(),query.getFolderId(),query.getFileName());
        //修改文件表
        file.setFileName(query.getFileName());
        file.setFolderId(query.getFolderId());
        fileDao.updateFile(file);

        log.info("文件修改完成，Query={}",query);

    }


    /**
     * 校验修改文件请求
     * @param query
     * @return
     */
    private SysFileDO verifyUpdateQuery(SysUpdateFileQuery query){
        SysUserDO user = UserContext.getUser();
        //判断文件名是否为空
        String fileName = query.getFileName().substring(0,query.getFileName().lastIndexOf("."));
        if(StringUtils.isEmpty(fileName)){
            throw new SysGlobalException(FILE_NAME_EMPTY.getCode(),FILE_NAME_EMPTY.getMsg());
        }
        //校验文件名是否重复
        SysFilePreCheckQuery preCheckQuery = new SysFilePreCheckQuery();
        preCheckQuery.setFileName(query.getFileName());
        preCheckQuery.setFolderId(query.getFolderId());
        SysFileDO file = fileDao.findOne(preCheckQuery,CREATE.name());
        if (Objects.nonNull(file)) {
            throw new SysGlobalException(FILE_REPEAT.getCode(), FILE_REPEAT.getMsg());
        }
        //判断文件是否存在
        file = fileDao.findById(query.getId(), user.getId(), CREATE.name());
        if(Objects.isNull(file)){
            throw new SysGlobalException(FILE_NOT_FOUND.getCode(),FILE_NOT_FOUND.getMsg());
        }

        return file;
    }


    /**
     * 管理员批量恢复文件
     * @param querys
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchRestoreAdmin(List<SysAdminRestoreQuery> querys) {
        //鉴权
        SysUserDO user = UserContext.getUser();
        if (!user.getRole().equals(ROOT.getCode())) {
            throw new SysGlobalException(USER_AUTH_ERR.getCode(), USER_AUTH_ERR.getMsg());
        }
        log.info("批量恢复文件开始querys:{}",querys);

        Set<Long> fileIds = querys.stream().map(SysAdminRestoreQuery::getFileId)
                .collect(Collectors.toSet());
        //修改文件表状态
        fileDao.batchUpdateFileState(fileIds,CREATE.name());
        //修改文件夹关系表状态
        folderRelationDao.batchUpdateState(fileIds,CREATE.name());
        recyclingDao.batchDelByFileIdsAdmin(Lists.newArrayList(fileIds));

        List<SysFileDO> files = fileDao.findByIds(fileIds, CREATE.name());
        files.forEach(file->{
            String uuid = UuidUtil.getShortUuid() + "_";
            SysUserDO sysUserDO = userDao.findByIdV2(file.getUserId());
            //修改文件表目录及名称
            fileDao.batchRestoreOrDel(Sets.set(file.getId()),sysUserDO.getRootId(),uuid);
            //修改文件夹关系表目录及名称
            folderRelationDao.batchRestoreOrDel(Sets.set(file.getId()),sysUserDO.getRootId(),uuid);
            //修改存储表
            SysStorageDO storage = storageDao.findByUserId(file.getUserId());
            Long size = file.getSize();
            String type = file.getType();
            storage.setCurrSize(storage.getCurrSize() + size);
            if (type.equals(DOCUMENT_FILE.name())) {
                storage.setDocumentSize(storage.getDocumentSize() + size);
            } else if (type.equals(VIDEO_FILE.name())) {
                storage.setVideoSize(storage.getVideoSize() + size);
            } else if (type.equals(IMAGE_FILE.name())) {
                storage.setImageSize(storage.getImageSize() + size);
            } else if (type.equals(OTHER_FILE.name())) {
                storage.setOrtherSize(storage.getOrtherSize() + size);
            }
            if(storage.getCurrSize()>storage.getMaxSize()){
                throw new SysGlobalException(SPACE_NOT_ENOUGH.getCode(),SPACE_NOT_ENOUGH.getMsg());
            }
            storageDao.update(storage);
        });

        log.info("批量恢复文件结束querys:{}",querys);

    }

    /**
     * 管理员批量删除文件
     * @param querys
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchdeleteAdmin(List<SysDeleteFileAdminQuery> querys) {
        //鉴权
        SysUserDO user = UserContext.getUser();
        if (!user.getRole().equals(ROOT.getCode())) {
            throw new SysGlobalException(USER_AUTH_ERR.getCode(), USER_AUTH_ERR.getMsg());
        }
        Set<Long> fileIds = querys.stream().map(SysDeleteFileAdminQuery::getFileId).collect(Collectors.toSet());

        //批量修改文件表状态
        fileDao.batchUpdateFileState(fileIds,DELETE.name());
        //批量修改文件关系表状态
        folderRelationDao.batchUpdateState(fileIds,DELETE.name());

        //添加OR修改回收站记录
        querys.forEach(query->{
            SysFileDO file = fileDao.findById(query.getFileId(), query.getUserId(), DELETE.name());
            if(Objects.isNull(file)){
                throw new SysGlobalException(FILE_NOT_FOUND.getCode(), FILE_NOT_FOUND.getMsg());
            }
            if(file.getState().equals(RECYCLING.name())){
                SysRecyclingDO recycling = recyclingDao.findByFileId(query.getFileId());
                recycling.setState(DELETE.name());
                SysRestoreOrDelQuery query1 = new SysRestoreOrDelQuery();
                query1.setFileIds(Lists.newArrayList(file.getId()));
                query1.setUserId(file.getUserId());
                recyclingDao.batchUpdateStateByFileIds(query1,DELETE.name());
            }else{
                SysRecyclingDO recycling = new SysRecyclingDO();
                recycling.setId(SnowFlakeIdWorker.nextId());
                recycling.setFileId(file.getId());
                recycling.setState(DELETE.name());
                recycling.setUserId(file.getUserId());
                recycling.setName(file.getFileName());
                recyclingDao.add(recycling);
                //修改存储信息表
                SysStorageDO storage = storageDao.findByUserId(query.getUserId());
                storage.setCurrSize(storage.getCurrSize() - file.getSize());
                if (file.getType().equals(DOCUMENT_FILE.name())) {
                    storage.setDocumentSize(storage.getDocumentSize() - file.getSize());
                } else if (file.getType().equals(VIDEO_FILE.name())) {
                    storage.setVideoSize(storage.getVideoSize() - file.getSize());
                } else if (file.getType().equals(IMAGE_FILE.name())) {
                    storage.setImageSize(storage.getImageSize() - file.getSize());
                } else if (file.getType().equals(OTHER_FILE.name())) {
                    storage.setOrtherSize(storage.getOrtherSize() - file.getSize());
                }
                if(storage.getCurrSize()>storage.getMaxSize()){
                    throw new SysGlobalException(SPACE_NOT_ENOUGH.getCode(),SPACE_NOT_ENOUGH.getMsg());
                }
                storageDao.update(storage);
            }


        });


    }

}
